package authblog

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"time"

	"blogcenter/internal/middleware"
	"blogcenter/internal/model"
	"blogcenter/internal/svc"
	"blogcenter/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"gorm.io/gorm"
)

type RecommendBlogLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewRecommendBlogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RecommendBlogLogic {
	return &RecommendBlogLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *RecommendBlogLogic) RecommendBlog(req *types.RecommendRquest) (resp *types.BlogListResponse, err error) {
	UserId, _ := l.ctx.Value(middleware.UserIdKey).(int)

	// 获取 id 列表
	blogs, err := l.recommendTypeReturn(UserId, req)
	if err != nil {
		return &types.BlogListResponse{
			BaseResponse: types.BaseResponse{
				Code:    500,
				Message: err.Error(),
			},
			Pagination: types.Pagination{},
			Data:       nil,
		}, nil
	}

	// 获取完整文章
	bloglist, err := l.getRecommendList(UserId, blogs)
	if err != nil {
		return &types.BlogListResponse{
			BaseResponse: types.BaseResponse{
				Code:    500,
				Message: err.Error(),
			},
			Pagination: types.Pagination{},
			Data:       nil,
		}, nil
	}

	return &types.BlogListResponse{
		BaseResponse: types.BaseResponse{
			Code:    200,
			Message: "获取成功",
		},
		Pagination: types.Pagination{
			Page:  req.Offset,
			Size:  req.Num,
			Total: len(bloglist),
		},
		Data: bloglist,
	}, nil
}

func (l *RecommendBlogLogic) recommendTypeReturn(userId int, req *types.RecommendRquest) ([]string, error) {
	switch req.RecommendType {
	case "0":
		return l.svcCtx.GorseClient.RecommendBlogGorse(l.ctx, userId, req.Category, int(req.Num), int(req.Offset))
	default:
		return nil, errors.New("推荐错误")
	}
}

func (l *RecommendBlogLogic) getRecommendList(userId int, blogId []string) ([]types.Blog, error) {
	var blogs []types.Blog
	for i := 0; i < len(blogId); i++ {
		blogIdInt, err := strconv.Atoi(blogId[i])
		if err != nil {
			return nil, err
		}
		blog, err := l.getBlogOnce(userId, blogIdInt)
		if err != nil {
			logx.Error(fmt.Errorf("根据推荐系统ID获取帖子失败: %w", err))
			continue
		}
		blogs = append(blogs, *blog)
	}
	return blogs, nil
}

func (l *RecommendBlogLogic) getBlogOnce(userId int, blogId int) (resp *types.Blog, err error) {
	var blogCache types.Blog
	cacheKey := fmt.Sprintf("%s%d", blogCacheKeyPrefix, blogId)
	cachedData, err := l.getFromCache(cacheKey, userId)
	if err != nil && err != redis.Nil {
		logx.Errorf("从缓存获取博客失败, key: %s, error: %v", cacheKey, err)
	}
	if cachedData != nil {
		blogCache = *cachedData
		return &blogCache, nil
	}

	// 缓存未命中，从数据库查询
	blog, images, err := l.svcCtx.BlogModel.FindWithImages(l.ctx, uint(blogId))
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("博客不存在")
		}
		logx.Errorf("查询博客详情失败, blogId: %d, error: %v", blogId, err)
		return nil, errors.New("获取博客详情失败")
	}
	// 获取点赞状态
	var isLiked bool
	if userId > 0 {
		// 先尝试从Redis获取点赞状态
		likeKey := fmt.Sprintf("%s%d:%d", blogLikeKeyPrefix, blogId, userId)
		if val, err := l.svcCtx.Redis.Get(likeKey); err == nil && val == "1" {
			isLiked = true
		} else if err != nil && err != redis.Nil {
			logx.Errorf("获取Redis点赞状态失败, key: %s, error: %v", likeKey, err)
		}

		// Redis未命中则查询数据库
		if !isLiked {
			var likeRecord model.BlogLike
			err := l.svcCtx.DB.Where("blog_id = ? AND user_id = ?", blogId, userId).
				First(&likeRecord).Error
			if err == nil && likeRecord.LikeAction == 1 {
				isLiked = true
				// 异步写入Redis缓存
				go func() {
					if err := l.svcCtx.Redis.Setex(likeKey, "1", int((24 * time.Hour).Seconds())); err != nil {
						logx.Errorf("写入点赞状态到Redis失败, key: %s, error: %v", likeKey, err)
					}
				}()
			} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
				logx.Errorf("查询数据库点赞状态失败, blogId: %d, userId: %d, error: %v",
					blogId, userId, err)
			}
		}
	}
	// 构造响应数据
	resp = &types.Blog{
		Id:         int(blog.ID),
		Title:      blog.Title,
		Content:    blog.Content,
		LikeNum:    blog.LikeNum,
		CommentNum: blog.CommentNum,
		Place:      blog.Place,
		JoyNum:     blog.JoyNum,
		JoyFeeling: blog.JoyFeeling,
		CreateTime: blog.CreatedAt.Format("2006-01-02 15:04:05"),
		Photo: types.PhotoData{
			Url: images,
		},
		UserId:  blog.UserId,
		ReadNum: blog.ReadNum,
		Liked:   isLiked, // 添加点赞状态
	}

	redisResp := &types.BlogDetailResponse{
		BaseResponse: types.BaseResponse{
			Code:    200,
			Message: "获取成功",
		},
		Data: *resp,
	}
	// 5. 异步更新缓存
	go func() {
		if err := l.setToCache(cacheKey, redisResp); err != nil {
			logx.Errorf("更新缓存失败, key: %s, error: %v", cacheKey, err)
		}
	}()
	return resp, nil

}

func (l *RecommendBlogLogic) setToCache(key string, data *types.BlogDetailResponse) error {
	jsonData, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("序列化缓存数据失败: %v", err)
	}

	if err := l.svcCtx.Redis.Setex(key, string(jsonData), int(cacheExpiration.Seconds())); err != nil {
		return err
	}

	return nil
}

// 从缓存中获取
func (l *RecommendBlogLogic) getFromCache(key string, userId int) (*types.Blog, error) {
	data, err := l.svcCtx.Redis.Get(key)
	if err != nil {
		return nil, err
	}

	var resp types.BlogDetailResponse
	if err := json.Unmarshal([]byte(data), &resp); err != nil {
		return nil, fmt.Errorf("反序列化缓存数据失败: %v", err)
	}

	// 如果用户已登录，补充点赞状态
	if userId > 0 {
		likeKey := fmt.Sprintf("%s%d:%d", blogLikeKeyPrefix, resp.Data.Id, userId)
		if val, err := l.svcCtx.Redis.Get(likeKey); err == nil && val == "1" {
			resp.Data.Liked = true
		}
	}

	return &types.Blog{
		Id:         resp.Data.Id,
		UserId:     resp.Data.UserId,
		Title:      resp.Data.Title,
		Content:    resp.Data.Content,
		Photo:      resp.Data.Photo,
		LikeNum:    resp.Data.LikeNum,
		JoyNum:     resp.Data.JoyNum,
		JoyFeeling: resp.Data.JoyFeeling,
		Place:      resp.Data.Place,
		CommentNum: resp.Data.CommentNum,
		CreateTime: resp.Data.CreateTime,
		ReadNum:    resp.Data.ReadNum,
		Liked:      resp.Data.Liked,
	}, nil
}
